package pacman.entries.pacman;

import java.util.ArrayList;
import java.util.List;

import pacman.controllers.Controller;
import pacman.game.Constants.MOVE;
import pacman.game.Game;
import pacman.ml.behaviors.*;
import pacman.ml.features.*;

/*
 * This is the class you need to modify for your entry. In particular, you need to
 * fill in the getAction() method. Any additional classes you write should either
 * be placed in this package or sub-packages (e.g., game.entries.pacman.mypackage).
 */
public class PerceptronPacMan extends Controller<MOVE>
{
	public PerceptronPacMan() {
		super();
		InitTestWeights();
	}
	
	private MOVE m_Move=MOVE.NEUTRAL;
	
	private List<FeatureExtractor> features;
	private List<Behavior> behaviors;
	private Behavior defaultBehavior;
	
	public MOVE getMove(Game game, long timeDue) 
	{
		//Place your game logic here to play the game as Ms Pac-Man
		ChooseMove(game);
		
		return m_Move;
	}
	
	// ______________________________________________ DECISION MAKING
	
	// There are N features and M behaviors.
	
	int N;
	int M;
	float[][] m_Weights;
	float[] m_FeatureValues;
	
	// float[][] m_Weights = new float[N][M]; // N x M array of weights from features to behaviors
	
	/*
	 * set behaviors and features here
	 */
	private void InitTestWeights() {
		
		features = new ArrayList<FeatureExtractor>(2);
		features.add(FeatureExtractor.getInstance(NonEdibleGhostDistanceThresholdFeature.class, 20));
		features.add(FeatureExtractor.getInstance(EdibleGhostsFeature.class, 0));
		// features.add(new NonEdibleGhostDistanceThresholdFeature(20));
		// features.add(new EdibleGhostsFeature());
		
		behaviors = new ArrayList<Behavior>();
		behaviors.add(new RunAwayFromGhostsBehavior());
		behaviors.add(new EatClosestEdibleGhostBehavior());
		defaultBehavior = new EatClosestPillBehavior();
		
		N = features.size();
		M = behaviors.size();
		
		float[][] weights = {{2,0},{0,1}};
		m_Weights = weights;   // initialize the weights
		m_FeatureValues = new float[N];
	}
	
	private void ChooseMove(Game game) {
		
		// Run the feature checks
		RunFeatures(game);
		
		// Multiply features weights and choose max
		int bestBehaviorIndex = 0;
		float bestWeight = 0.0f;
		
		for (int j = 0; j < M; j++) { // for each behavior
			float weight = 0.0f;
			for (int i = 0; i < N; i++) // tally the feature-weight sums for that behavior
				weight += m_Weights[i][j] * m_FeatureValues[i]; 
			
			if (weight > bestWeight) { // if has a better sum, select it 
				bestWeight = weight;
				bestBehaviorIndex = j;
			}
		}
		
		// Run appropriate behavior to set m_Move
		if (bestWeight > 0.0f)
			m_Move = RunBehavior(bestBehaviorIndex, game);
		else
			m_Move = RunBehavior(-1, game);
	}
	
	// ______________________________________________ FEATURES
	
	private void RunFeatures(Game game) {
		int i = 0;
		for(FeatureExtractor feature : features) {
			m_FeatureValues[i++] = feature.GetValue(game);
		}
	}
	
	// ______________________________________________ BEHAVIORS
	
	private MOVE RunBehavior(int behaviorIndex, Game game) {
		
		Behavior behavior;
		try {
			behavior = behaviors.get(behaviorIndex);
		}
		catch(IndexOutOfBoundsException e) {
			behavior = defaultBehavior;
		}
		return behavior.decide(game);
	}
	
}